package org.duang.push;

import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

import org.duang.common.exceptios.ServiceException;
import org.duang.http.util.HttpUtil;
import org.duang.kit.ToolsKit;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.ChildChannelStateEvent;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelHandler;
import org.jboss.netty.handler.codec.http.DefaultHttpResponse;
import org.jboss.netty.handler.codec.http.HttpHeaders;
import org.jboss.netty.handler.codec.http.HttpMethod;
import org.jboss.netty.handler.codec.http.HttpRequest;
import org.jboss.netty.handler.codec.http.HttpResponseStatus;
import org.jboss.netty.handler.codec.http.HttpVersion;
import org.jboss.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
import org.jboss.netty.handler.codec.http.websocketx.PingWebSocketFrame;
import org.jboss.netty.handler.codec.http.websocketx.PongWebSocketFrame;
import org.jboss.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import org.jboss.netty.handler.codec.http.websocketx.WebSocketFrame;
import org.jboss.netty.handler.codec.http.websocketx.WebSocketServerHandshaker;
import org.jboss.netty.handler.codec.http.websocketx.WebSocketServerHandshakerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.aliyun4j.utils.Tools;

public class WebSockerChannelHandler extends SimpleChannelHandler {

	private static Logger logger = LoggerFactory.getLogger(WebSockerChannelHandler.class);
	private static String USERID_FIELD = "userid";

	// 新建连接
	@Override
	public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
		logger.debug("channelConnected: " + ctx.getChannel().getId(), e.getValue());
	}

	// 断开连接时
	@Override
	public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
		logger.debug("channelDisconnected: " + ctx.getChannel().getAttachment(), e.getValue());
		super.channelDisconnected(ctx, e);
//		removeChannel(ctx.getChannel()); //不能写这行代码，如果写了，重刷时会出现以下异常代码：
//		at com.alibaba.fastjson.serializer.ObjectFieldSerializer.writeProperty(ObjectFieldSerializer.java:70)
//		at com.alibaba.fastjson.serializer.JavaBeanSerializer.write(JavaBeanSerializer.java:225)
//		at com.alibaba.fastjson.serializer.ObjectFieldSerializer.writeValue(ObjectFieldSerializer.java:129)
	}

	// 关闭连接时
	@Override
	public void childChannelClosed(ChannelHandlerContext ctx, ChildChannelStateEvent e) throws Exception {
		logger.debug("childChannelClosed: " + ctx.getChannel().getAttachment(), e.getFuture());
		removeChannel(ctx.getChannel());
		super.childChannelClosed(ctx, e);
	}

	// 抛出异常时
	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
		Channel channel = ctx.getChannel();
		if (Tools.isNotEmpty(channel)) {
			try {
				logger.debug("exceptionCaught: " + e.getCause().getMessage() + " channelId: " + channel.getAttachment());
				removeChannel(channel);
			} catch (Exception ex) {
				logger.debug("exceptionCaught CHANNEL_MAP remove channel is fail:  channel.getAttachment() is  null!!");
			}
			channel.close();
		}
		// super.exceptionCaught(ctx, e);
	}

	// 删除WebSocket.CHANNEL_MAP里对应的channel
	private void removeChannel(Channel channel) {
		if (Tools.isNotEmpty(channel.getId())) {
			WebSocket.CHANNEL_MAP.remove(channel);
			logger.debug("remove " + channel.getAttachment() + " at CHANNEL_MAP  success!!  "
					+ ToolsKit.toJsonString(WebSocket.CHANNEL_MAP));
		}
	}

	@Override
	public void messageReceived(ChannelHandlerContext ctx, MessageEvent event) throws Exception {
		Channel channel = event.getChannel();
		Object message = event.getMessage();
		try {
			if (message instanceof HttpRequest) {
				processHttpRequest(channel, (HttpRequest) message); // 首先进行握手处理
			} else if (message instanceof WebSocketFrame) {
				processWebSocketFrame(channel, (WebSocketFrame) message); // 消息推送处理
			} else {
				throw new ServiceException("未知类型");
			}
		} catch (Exception ex) {
			logger.warn(ex.getMessage(), ex);
			channel.close();
		}
		super.messageReceived(ctx, event);
	}

	private void processHttpRequest(Channel channel, HttpRequest request) {
		if (!request.getMethod().equals(HttpMethod.GET)) {
			DefaultHttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1,
					HttpResponseStatus.BAD_REQUEST);
			channel.write(response);
			channel.close();
			return;
		}
		WebSocketServerHandshakerFactory wsShakerFactory = new WebSocketServerHandshakerFactory(
				"ws://" + request.getHeader(HttpHeaders.Names.HOST), null, false);
		WebSocketServerHandshaker wsShakerHandler = wsShakerFactory.newHandshaker(request);
		if (ToolsKit.isEmpty(wsShakerHandler)) {
			logger.warn("无法处理的websocket版本");
			wsShakerFactory.sendUnsupportedWebSocketVersionResponse(channel);
		} else {
			// 向客户端发送websocker握手，完成握手，客户端收到的状态是101 sitching protocol
			logger.debug("duang push shake hands success! channelId: " + channel.getId());
			wsShakerHandler.handshake(channel, request);
		}
	}

	private void put2Map(Channel channel) {
		String flag = (String) channel.getAttachment(); 
		// 如果有传userid过来的话，flag就是userid, 如果没有的话，flag就是Channel.getId()
		try {
			if (WebSocket.CHANNEL_MAP.containsKey(flag)) {
				WebSocket.CHANNEL_MAP.remove(flag);
			}
			WebSocket.CHANNEL_MAP.put(flag, channel);
		} catch (Exception e) {
			logger.debug(e.getMessage(), e);
		}
	}

	private void processWebSocketFrame(Channel channel, WebSocketFrame frame) {
		if (frame instanceof CloseWebSocketFrame) {
			channel.close();
		} else if (frame instanceof PingWebSocketFrame) {
			channel.write(new PongWebSocketFrame(frame.getBinaryData()));
		} else if (frame instanceof TextWebSocketFrame) {
			TextWebSocketFrame textRequest = (TextWebSocketFrame) frame;
			if (textRequest.getRsv() == 0) { // 第一次握手成功后返回消息
				String shakeHandStr = ((TextWebSocketFrame) frame).getText();
				String userid = channel.getId().toString();
				String text = "hello laotang";
				if (ToolsKit.isNotEmpty(shakeHandStr)) {
					Map<String, Object> tmpMap = HttpUtil.parseUrlParam2Map(shakeHandStr);
					userid = tmpMap.get(USERID_FIELD) + "";		//标识
					text = ToolsKit.toJsonString(ToolsKit.buildReturnDto(null, tmpMap));
				}
				channel.setAttachment(userid);//设置自定义参数
				if (ToolsKit.isNotEmpty(channel)) {
					put2Map(channel);
				}
				textRequest.setText(text);
			}
			if (ToolsKit.isEmpty(channel)) {
				logger.debug("WebSockerChannelHandler.processWebSocketFrame channel is null!");
				return;
			}
			try {
				logger.debug("send channelId: " + channel.getAttachment());
				channel.write(textRequest);
			} catch (Exception e) {
				logger.debug(e.getMessage(), e);
			}

		}
	}

	private void broadcastSend(WebSocketFrame frame) throws Exception {
		for (Iterator<Entry<String, Channel>> it = WebSocket.CHANNEL_MAP.entrySet().iterator(); it.hasNext();) {
			Entry<String, Channel> entry = it.next();
			Channel channel = entry.getValue();
			if (channel.isOpen() && channel.isConnected()) {
				processWebSocketFrame(channel, frame);
			}
		}
	}
	
	public void send(String[] ids, WebSocketFrame frame) throws Exception {
		if(ToolsKit.isNotEmpty(ids) && ids.length > 0) {
			multiIdSend(ids, frame);
		} else {
			broadcastSend(frame);
		}
	}
	
	private void multiIdSend(String[] ids, WebSocketFrame frame) throws Exception {
			for(String id : ids){
				Channel channel = WebSocket.CHANNEL_MAP.get(id);
				if(ToolsKit.isNotEmpty(channel) && channel.isOpen() && channel.isConnected()){
					processWebSocketFrame(channel, frame);
				}
			}
	}

}
